<!DOCTYPE html>
<html>
<head>
  <title>Documentation.md</title>
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/github-markdown-css/2.4.1/github-markdown.min.css">
</head>
<style>
    .markdown-body {
        box-sizing: border-box;
        min-width: 200px;
        max-width: 980px;
        margin: 0 auto;
        padding: 45px;
    }
</style>
<body>
<article class="markdown-body">
<h1 id="arc-接口说明文档">ARC 接口说明文档</h1>
<h2 id="目录">目录</h2>
<ol>
<li><a href="#综述">综述</a></li>
<li><a href="#识别系统">识别系统</a></li>
<li><a href="#api">API</a><ol>
<li><a href="#系统状态接口">系统状态接口</a></li>
<li><a href="#固件升级接口">固件升级接口</a></li>
<li><a href="#相机接口">相机接口</a></li>
<li><a href="#调用实例接口">调用实例接口</a></li>
<li><a href="#识别结果接口">识别结果接口</a></li>
<li><a href="#组管理接口">组管理接口</a></li>
<li><a href="#用户管理接口">用户管理接口</a></li>
<li><a href="#静态识别接口">静态识别接口</a></li></ol></li>
<li><a href="#错误代码">错误代码</a></li>
</ol>
<h2 id="综述">综述</h2>
<p>ARC 提供一系列 HTTP 接口，供开发者进行人脸识别相关应用开发。所有接口均通过 HTTP 进行调用，使用者可以通过 HTTP 状态码来判断调用是否成功，关于错误代码的详细说明参见下面<a href="#错误代码">章节</a>。</p>
<h2 id="识别系统">识别系统</h2>
<h3 id="基本概念">基本概念</h3>
<ul>
<li><strong>Track</strong> 是一段视频中，一张人脸从出现到离开，所有的帧的合集。</li>
<li><strong>Feature</strong> 是根据一张人脸图片抽取的特征，一般是 Base64 格式的字符串。</li>
<li><strong>底库</strong> 是待识别的人的集合。每个待识别的人由若干特征表示。</li>
<li><strong>Quality</strong> 是一个 0~1 之间的数字，表示人脸的质量（模糊，正脸等）。一般认为 0.5 以上可接受，0.8 以上优秀。一般来说业务逻辑端无需判断该数值。</li>
<li><strong>Confidence</strong> 是一个 0~100 之间的数字，表示识别置信度。一般无需业务逻辑端判断。</li>
</ul>
<h3 id="arc-的基本使用方式">ARC 的基本使用方式</h3>
<p>要使用 ARC 搭建一个人脸识别系统，需要相机和底库两个基本组成部分，一般按照以下步骤：</p>
<ol>
<li>调用 <code>group</code>, <code>user</code> 接口创建底库并添加用户，上传用户头像；</li>
<li>调用 <code>camera</code> 接口创建并配置相机；</li>
<li>调用 <code>handle</code> 接口创建调用实例并绑定相机和底库；</li>
<li>调用 <code>handle</code> 中的识别接口，开始识别并获得实时结果。</li>
</ol>
<h2 id="api">API</h2>
<h3 id="系统状态接口">系统状态接口</h3>
<p>获得 ARC 运行状态的相关接口。</p>
<h4 id="get-status"><code>GET /status</code></h4>
<p>获取运行状态。</p>
<p><strong>输入：</strong> 无</p>
<p><strong>输出：</strong></p>
<p>成功则返回 HTTP code 200。</p>
<p>内容为一个 json，格式如下：</p>
<pre><code>{
  "version": "version string",            // 当前 ARC 版本
  "time":    "2017-01-31 15:04:05+08:00", // 系统时间
  "disk":    {                            // 存储空间信息
    "total": 10000,                       // 总硬盘空间，单位为KiB
    "used":  9900,                        // 已用空间
    "avail": 100                          // 剩余空间
  },
  "ip":      {                            // 网络配置信息
    "addr":    "123.123.0.123",           // IP 地址
    "netmask": "255.255.255.0",           // 子网掩码
    "gateway": "123.123.0.1",             // 网关地址
    "dns":     "12.123.123.123",          // DNS 地址
    "dhcp":    true                       // 是否使用 DHCP，true 表示启用，false 表示手动设置地址
  }
}</code></pre>
<h3 id="固件升级接口">固件升级接口</h3>
<h4 id="post-firmwarepush"><code>POST /firmware/push</code></h4>
<p>上传指定的固件进行升级。</p>
<p><strong>输入：</strong></p>
<p>通过 <code>multipart/form-data</code> 方式上传一个升级文件。POST form 的 key 为 <code>file</code>。</p>
<pre><code>"file" // 升级包</code></pre>
<p><strong>输出：</strong></p>
<p>成功后系统会重启，导致暂时无法连接，等待一段时间后，可以通过调用 <code>/status</code> 系统状态接口，检查系统版本。</p>
<h4 id="post-firmwaretime"><code>POST /firmware/time</code></h4>
<p>设置系统时间。</p>
<p><strong>输入：</strong></p>
<p>通过 POST form 输入要设定的时间，key 为 <code>time</code>，时间格式如下（暂不支持修改时区）。</p>
<pre><code>"time": "2006-01-23 15:04:05+08:00"</code></pre>
<p><strong>输出：</strong></p>
<p>成功则返回 HTTP code 200。</p>
<p>内容为 json，格式如下：</p>
<pre><code>{
  "time": "2006-01-23 15:04:05+08:00"
}</code></pre>
<h4 id="post-firmwareip"><code>POST /firmware/ip</code></h4>
<p>配置网络 IP 地址、子网掩码、网关、DNS 服务器，或使用 DHCP 自动获取 IP 地址。</p>
<p><strong>输入：</strong></p>
<p>通过 POST form 传入一个 key 为 <code>ip</code> 的 json，格式如下（可以只选择其中一部分设置）：</p>
<pre><code>"ip":
{
  "addr":    "123.123.0.123",  // IP 地址
  "netmask": "255.255.255.0",  // 子网掩码
  "gateway": "123.123.0.1",    // 网关地址
  "dns":     "12.123.123.123", // DNS 地址
  "dhcp":    true              // 是否使用 DHCP，true 表示启用，false 表示手动设置地址，如果启用了 DHCP，其他设置均无效
}</code></pre>
<p><strong>输出：</strong></p>
<p>成功则返回 HTTP code 200。</p>
<p>返回新的 IP 配置信息，为 json：</p>
<pre><code>{
  "addr":    "123.123.0.123",  // IP 地址
  "netmask": "255.255.255.0",  // 子网掩码
  "gateway": "123.123.0.1",    // 网关地址
  "dns":     "12.123.123.123", // DNS 地址
  "dhcp":    true              // 是否使用 DHCP，true 表示启用，false 表示手动设置地址
}</code></pre>
<h3 id="相机接口">相机接口</h3>
<h4 id="get-camera"><code>GET /camera</code></h4>
<p>获取所有相机的设置和状态。</p>
<p><strong>输入：</strong> 无</p>
<p><strong>输出：</strong></p>
<p>成功则返回 HTTP code 200。</p>
<p>内容为 json，其中是所有相机的列表：</p>
<pre><code>{
  "cameras": [
    {
      "id":   0,                            // ID
      "name": "camera1",                    // 名称
      "url":  "http://123.123.0.1/camera1", // URL
      "attached_params": {                  // 其他参数
        "key1": "value1",
        "key2": "value2"
      }
    },
    {
      "id":   1,
      "name": "camera2",
      "url":  "http://123.123.0.1/camera2",
      "attached_params": {
        "key1": "value1",
        "key2": "value2"
      }
    },
    ...
  ]
}</code></pre>
<h4 id="get-cameracamera_id"><code>GET /camera/&lt;camera_id&gt;</code></h4>
<p>获取 <code>camera_id</code> 的相机的设置。</p>
<p><strong>输入：</strong></p>
<p>URL 参数 <code>camera_id</code>。</p>
<p><strong>输出：</strong></p>
<p>成功则返回 HTTP code 200。</p>
<pre><code>{
  "id":   0,                            // ID
  "name": "camera1",                    // 名称
  "url":  "http://123.123.0.1/camera1", // URL
  "attached_params": {                  // 其他参数
    "key1": "value1",
    "key2": "value2"
  }
}</code></pre>
<h4 id="post-camera"><code>POST /camera</code></h4>
<p>设置一台新的相机。</p>
<p><strong>输入：</strong></p>
<p>通过 POST form 提供以下参数：</p>
<pre><code>"name": "camera1"                    // 名称
"url":  "http://123.123.0.1/camera1" // URL
"attached_params": {                 // 其他参数，可选
  "key1": "value1",
  "key2": "value2"
}</code></pre>
<p><strong>输出：</strong></p>
<p>成功则返回 HTTP code 201。</p>
<p>内容是新创建的相机的设置。</p>
<pre><code>{
  "id":   0,                            // ID
  "name": "camera1",                    // 名称
  "url":  "http://123.123.0.1/camera1", // URL
  "attached_params": {                  // 其他参数
    "key1": "value1",
    "key2": "value2"
  }
}</code></pre>
<h4 id="put-cameracamera_id"><code>PUT /camera/&lt;camera_id&gt;</code></h4>
<p>修改 <code>camera_id</code> 的相机设置。</p>
<p><strong>输入：</strong></p>
<p>URL 参数 <code>camera_id</code>。</p>
<p>通过 POST form 提供以下参数：</p>
<pre><code>"name": "camera1"                    // 名称
"url":  "http://123.123.0.1/camera1" // URL
"attached_params": {                 // 其他参数，可选
  "key1": "value1",
  "key2": "value2"
}</code></pre>
<p><strong>输出：</strong></p>
<p>成功则返回 HTTP code 201。</p>
<p>内容是相机的设置。</p>
<pre><code>{
  "id":   0,                            // ID
  "name": "camera1",                    // 名称
  "url":  "http://123.123.0.1/camera1", // URL
  "attached_params": {                  // 其他参数
    "key1": "value1",
    "key2": "value2"
  }
}</code></pre>
<h4 id="delete-cameracamera_id"><code>DELETE /camera/&lt;camera_id&gt;</code></h4>
<p>删除 <code>camera_id</code> 的相机。</p>
<p><strong>输入：</strong></p>
<p>URL 参数 <code>camera_id</code>。</p>
<p><strong>输出：</strong></p>
<p>成功则返回 HTTP code 204。</p>
<h3 id="调用实例接口">调用实例接口</h3>
<p>用于设置 Video 接口的调用实例。一个 Video 调用实例，包含一个相机 <code>camera_id</code> 和一个底库 <code>group_id</code>。</p>
<h4 id="get-handle"><code>GET /handle</code></h4>
<p>返回所有已设置的 Video 调用实例，以及它们的信息。</p>
<p><strong>输入：</strong> 无</p>
<p><strong>输出：</strong></p>
<p>成功则返回 HTTP code 200。</p>
<p>内容为 json，其中是所有调用实例的列表：</p>
<pre><code>{
  "handles": [
    {
      "id": 0,
      "camera_id": 0,
      "group_id": "group id",
      "working": true,
      "attached_params": {
        ...
      }
    },
    ...
  ]
}</code></pre>
<h4 id="get-handlehandle_id"><code>GET /handle/&lt;handle_id&gt;</code></h4>
<p>获取 <code>handle_id</code> 的 Video 调用实例的信息。</p>
<p><strong>输入：</strong></p>
<p>URL 参数 <code>handle_id</code>。</p>
<p><strong>输出：</strong></p>
<p>成功则返回 HTTP code 200。</p>
<pre><code>{
  "id": 0,
  "camera_id": 0,
  "group_id": "group id",
  "working": true,
  "attached_params": {
    ...
  }
}</code></pre>
<h4 id="post-handle"><code>POST /handle</code></h4>
<p>新建一个 Video 调用实例。</p>
<p><strong>输入：</strong></p>
<p>通过 POST form 提供以下参数：</p>
<pre><code>"camera_id": 0
"group_id": "group id"
"attached_params": {   // 可选
  ...
}</code></pre>
<p><strong>输出：</strong></p>
<p>成功则返回 HTTP code 201。</p>
<pre><code>{
  "id": 0,
  "camera_id": 0,
  "group_id": "group id",
  "working": true,
  "attached_params": {
    ...
  }
}</code></pre>
<h4 id="put-handlehandle_id"><code>PUT /handle/&lt;handle_id&gt;</code></h4>
<p>修改 Video 调用实例。如果 Video 调用实例已被打开，这会导致该实例停止。</p>
<p><strong>输入：</strong></p>
<p>URL 参数 <code>handle_id</code>。</p>
<p>通过 POST form 提供以下参数：</p>
<pre><code>"camera_id": 0
"group_id": "group id"
"attached_params": {   // 可选
  ...
}</code></pre>
<p><strong>输出：</strong></p>
<p>成功则返回 HTTP code 201。</p>
<pre><code>{
  "id": 0,
  "camera_id": 0,
  "group_id": "group id",
  "working": true,
  "attached_params": {
    ...
  }
}</code></pre>
<h4 id="delete-handlehandle_id"><code>DELETE /handle/&lt;handle_id&gt;</code></h4>
<p>删除 Video 调用实例。如果 Video 调用实例已被打开，这会导致该实例停止。</p>
<p><strong>输入：</strong></p>
<p>URL 参数 <code>handle_id</code>。</p>
<p><strong>输出：</strong></p>
<p>成功则返回 HTTP code 204。</p>
<h4 id="post-handlehandle_idstart"><code>POST /handle/&lt;handle_id&gt;/start</code></h4>
<p>打开 Video 调用实例。</p>
<p><strong>输入：</strong></p>
<p>URL 参数 <code>handle_id</code>。</p>
<p><strong>输出：</strong></p>
<p>成功则返回 HTTP code 200。</p>
<h4 id="post-handlehandle_idstop"><code>POST /handle/&lt;handle_id&gt;/stop</code></h4>
<p>停止 Video 调用实例。</p>
<p><strong>输入：</strong></p>
<p>URL 参数 <code>handle_id</code>。</p>
<p><strong>输出：</strong></p>
<p>成功则返回 HTTP code 200。</p>
<h4 id="websocket-handlehandle_idlive"><code>WEBSOCKET /handle/&lt;handle_id&gt;/live</code></h4>
<p>对于打开的 Video 调用实例，获取它接口的结果。</p>
<p><strong>输入：</strong></p>
<p>URL 参数 <code>handle_id</code>。</p>
<p><strong>输出：</strong></p>
<p>通过 WEBSOCKET 实时接收识别结果。</p>
<p>识别结果消息为 json，格式如下：</p>
<pre><code>{
  "track": 7,
  "timestamp": 1484538428345487480,
  "quality": 0.975,
  "rect": {
    "left": 1444,
    "top": 88,
    "right": 1596,
    "bottom": 242
  },
  "image": "Base64 encoded JPEG image",
  "snapshot_status": "ongoing",
  "feature": "A_FEATURE",
  "person": {
    "id": "PERSON_ID_1",
    "tag": "PERSON_TAG_1",
    "confidence": 96.209435,
    "feature_id": 1
  },
  "recognize_status": "recognized"
}</code></pre>
<p>其中，<code>feature</code> 表示这张人脸的 Feature，<code>person</code> 中 <code>id</code> 和 <code>tag</code> 为底库中与之最相像的用户的信息（见<a href="#用户管理接口">用户接口</a>），<code>recognize_status</code> 表示程序对这张人脸的判定，在 <code>stranger</code>, <code>not_determined</code> 和 <code>recognized</code> 中取值，分别表示陌生人，未定和已识别。如果 <code>recognize_status</code> 为 <code>recognized</code>，说明程序认为该人脸就是 <code>person</code> 中指示的用户。</p>
<p>如果 <code>snapshot_status</code> 为 <code>ongoing</code>，则程序发送对实时人脸的计算结果。如果 <code>recognize_status</code> 不为 <code>not_determined</code>，说明已经判定完毕，则程序不再发送后续的实时人脸计算结果。</p>
<p>如果 <code>snapshot_status</code> 为 <code>gone</code>，说明该 Track 已经结束。程序会发送质量最好的人脸的计算结果。</p>
<h4 id="post-handlehandle_idverify"><code>POST /handle/&lt;handle_id&gt;/verify</code></h4>
<p>检查视频画面中是否有图像或 feature 中的人物。</p>
<p><strong>输入：</strong></p>
<p>URL 参数 <code>handle_id</code>。</p>
<p>POST form：</p>
<pre><code>"image" // 'multipart/form-data' 上传的图片文件</code></pre>
<p>或者</p>
<pre><code>"feature": "xxxxxxxxx" // feature 字符串</code></pre>
<p><strong>输出：</strong></p>
<pre><code>{
  "face_info": {
    "rect": {
      "left": 197,
      "top": 454,
      "right": 752,
      "bottom": 1009
    },
  }
  "result": {
    "confidence": 92.330423,
    "result": {
      "track": 7,
      "quality": 0.975,
      "rect": {
        "left": 688,
        "top": 394,
        "right": 1028,
        "bottom": 734
      },
      "image": "Base64 encoded JPEG image"
    },
    "recognized": true
  },
}</code></pre>
<p><code>result.confidence</code> 代表置信度，<code>result.result</code> 代表检查结果，<code>result.recognized</code> 代表是否与图像或 Feature 中的人物匹配。<code>face_info</code> 代表输入图像的信息。</p>
<p>该接口会首先检查不早于请求时刻 <code>time.gate_reserve</code> 时间之前的人脸，如果这段时间内包含匹配的人脸，就直接输出；如果没有，就等待最多 <code>time.gate_wait</code> 时间；如果仍没有，就输出最相似的人脸。这两个时间可以设置（暂未实现配置接口）。</p>
<p>如果在这段时间内没有出现任何人脸，则 <code>result.confidence</code> 和 <code>result.result</code> 均不存在。</p>
<h3 id="识别结果接口">识别结果接口</h3>
<h4 id="get-result"><code>GET /result</code></h4>
<p>查询识别结果。</p>
<p><strong>输入：</strong></p>
<p>POST form，其中 <code>query</code> 是查询条件，无查询条件则返回所有结果，<code>skip</code> 是跳过的记录数，<code>limit</code> 是最多显示的记录数。</p>
<pre><code>"query":                    // 查询条件，所有条件都是可选的
{
  "handle_ids": [0, 1, 2],  // 调用实例 ID
  "user_ids": [             // 用户 ID
    "xxxxxx",
    "yyyyyy",
    ...
  ],
  "status": [               // 结果状态，"not_determined", "recognized", "stranger" 中的一种或多种
    "not_determined",
    "recognized",
    "stranger"
  ],
  "timestamp_min": 0,       // 指定时间段
  "timestamp_max": 11111111
}

"skip": 0  // 可选
"limit" 50 // 可选</code></pre>
<p><strong>输出：</strong></p>
<p>成功则返回 HTTP code 200。</p>
<pre><code>{
  "results": [
    {
      "handle_id": 0,
      "status": "recognized",
      "user_id": "xxxx",
      "user_tag": {},
      "timestamp": 11111111,

      // 通过质量判断的，confidence 最大的一张图
      "snapshot": "string", // 抓拍图，Base64 JPEG
      "confidence": 0.8,
      "quality": 0.9
    },
    ...
  ]
}</code></pre>
<h4 id="delete-resulthandle_id"><code>DELETE /result/&lt;handle_id&gt;</code></h4>
<p>清空 <code>handle_id</code> 的识别结果。</p>
<p><strong>输入：</strong></p>
<p>URL 参数 <code>handle_id</code>。</p>
<p><strong>输出：</strong></p>
<p>成功则返回 HTTP code 204。</p>
<h4 id="delete-result"><code>DELETE /result</code></h4>
<p>清空所有识别结果。</p>
<p><strong>输入：</strong></p>
<p>无</p>
<p><strong>输出：</strong></p>
<p>成功则返回 HTTP code 204。</p>
<h3 id="组管理接口">组管理接口</h3>
<h4 id="get-group"><code>GET /group</code></h4>
<p>获取所有组的列表。</p>
<p><strong>输入：</strong></p>
<p>URL 查询（在 URL 最后添加'?'开头的查询），<code>skip</code> 是跳过的记录数，<code>limit</code> 是最多显示的记录数，这两个参数可选。</p>
<pre><code>"skip": 0   // 可选
"limit": 50 // 可选</code></pre>
<p><strong>输出：</strong></p>
<p>成功则返回 HTTP code 200。内容为 json。</p>
<p>注：在返回的组的信息中，<code>users</code> 键（组的用户列表）为空，若要获取用户列表，请使用 <code>GET /group/&lt;group_id&gt;</code> 接口。</p>
<pre><code>{
  "groups": [
    {
      "id": "xxxxxxx",
      "tag": {
        "key1": "value1",
        "key2": "value2"
      },
      "users": [],
      "user_count": 10
    },
    ...
  ]
}</code></pre>
<h4 id="post-group"><code>POST /group</code></h4>
<p>新建一个组。</p>
<p><strong>输入：</strong></p>
<p>POST form，<code>tag</code> 是一个任意的 json，表示这个组附加的自定义信息。</p>
<pre><code>"tag":              // 可选
{
  "key1": "value1",
  "key2": "value2"
}</code></pre>
<p><strong>输出：</strong></p>
<p>成功则返回 HTTP code 201。</p>
<p>内容为新创建的组的信息。</p>
<pre><code>{
  "id": "xxxxxxx",
  "tag": {
    "key1": "value1",
    "key2": "value2"
  },
  "users": [],
  "user_count": 0
}</code></pre>
<h4 id="get-groupgroup_id"><code>GET /group/&lt;group_id&gt;</code></h4>
<p>获取 <code>group_id</code> 的组的信息，以及组中的用户列表。</p>
<p><strong>输入：</strong></p>
<p>URL 参数 <code>group_id</code>。</p>
<p><strong>输出：</strong></p>
<p>成功则返回 HTTP code 200。</p>
<pre><code>{
  "id": "xxxxxxx",
  "tag": {
    "key1": "value1",
    "key2": "value2"
  },
  "users": [
    "aaaaaaa",
    "bbbbbbb",
    ...
  ],
  "user_count": 10
}</code></pre>
<h4 id="put-groupgroup_id"><code>PUT /group/&lt;group_id&gt;</code></h4>
<p>修改 <code>group_id</code> 的组的信息。</p>
<p><strong>输入：</strong></p>
<p>URL 参数 <code>group_id</code>。</p>
<p>POST form <code>tag</code> 是要修改成的新信息。</p>
<pre><code>"tag":
{
  "key1": "value1",
  "key2": "value2"
}</code></pre>
<p><strong>输出：</strong></p>
<p>成功则返回 HTTP code 201。</p>
<pre><code>{
  "id": "xxxxxxx",
  "tag": {
    "key1": "value1",
    "key2": "value2"
  },
  "users": [
    "aaaaaaa",
    "bbbbbbb",
    ...
  ],
  "user_count": 10
}</code></pre>
<h4 id="delete-groupgroup_id"><code>DELETE /group/&lt;group_id&gt;</code></h4>
<p>删除 <code>group_id</code> 的组。</p>
<p><strong>输入：</strong></p>
<p>URL 参数 <code>group_id</code>。</p>
<p><strong>输出：</strong></p>
<p>成功则返回 HTTP code 204。</p>
<h4 id="post-groupgroup_iduser_id"><code>POST /group/&lt;group_id&gt;/&lt;user_id&gt;</code></h4>
<p>在 <code>group_id</code> 组中添加用户 <code>user_id</code>。</p>
<p><strong>输入：</strong></p>
<p>URL 参数 <code>group_id</code>, <code>user_id</code>。</p>
<p><strong>输出：</strong></p>
<p>成功则返回 HTTP code 201。</p>
<pre><code>{
  "id": "xxxxxxx",
  "tag": {
    "key1": "value1",
    "key2": "value2"
  },
  "users": [
    "aaaaaaa",
    "bbbbbbb",
    ...
  ],
  "user_count": 10
}</code></pre>
<h4 id="delete-groupgroup_iduser_id"><code>DELETE /group/&lt;group_id&gt;/&lt;user_id&gt;</code></h4>
<p>在 <code>group_id</code> 组中删除用户 <code>user_id</code>。</p>
<p><strong>输入：</strong></p>
<p>URL 参数 <code>group_id</code>, <code>user_id</code>。</p>
<p><strong>输出：</strong></p>
<p>成功则返回 HTTP code 200。</p>
<pre><code>{
  "id": "xxxxxxx",
  "tag": {
    "key1": "value1",
    "key2": "value2"
  },
  "users": [
    "aaaaaaa",
    "bbbbbbb",
    ...
  ],
  "user_count": 10
}</code></pre>
<h4 id="get-group_query"><code>GET /group_query</code></h4>
<p>通过条件查询组。</p>
<p>可以通过 <code>tag</code> 及 <code>user_ids</code> 进行查询。<code>user_ids</code> 指定组所包含的用户，包含 <code>user_ids</code>中的任何一个用户即满足条件。<code>tag</code> 查询组的 <code>tag</code> 中包含的内容，可以指定简单的 (key, value) 对，也可以指定 (key, list)，其中 <code>list</code> 是一个列表，<code>tag</code> 中的 <code>key</code> 要包含所有 <code>list</code> 中的成员。<code>tag</code> 的查询也可采用 MongoDB 的查询语法，指定 (key, object)，其中 <code>object</code> 是一个 MongoDB 的查询。<code>tag</code> 查询的所有 key 之间取交集。</p>
<p><strong>输入：</strong></p>
<p>URL 查询（在 URL 最后添加'?'开头的查询），包含以下参数：</p>
<pre><code>"query": // 可选
{
    "tag": {
      "key1": "value1",
      "key2": ["value2", "value3"]
    },
    "user_ids": ["id1", "id2", "id3"]
}

"skip": 0   // 可选
"limit": 50 // 可选</code></pre>
<p><strong>输出：</strong></p>
<p>成功则返回 HTTP code 200。</p>
<p>注：在返回的组的信息中，<code>users</code> 键（组的用户列表）为空，若要获取用户列表，请使用 <code>GET /group/&lt;group_id&gt;</code> 接口。</p>
<pre><code>{
  "groups": [
    {
      "id": "xxxxxxx",
      "tag": {
        "key1": "value1",
        "key2": ["value2", "value3"]
      },
      "users": [],
      "user_count": 10
    },
    ...
  ]
}</code></pre>
<h3 id="用户管理接口">用户管理接口</h3>
<h4 id="get-user"><code>GET /user</code></h4>
<p>获取所有用户列表。</p>
<p><strong>输入：</strong></p>
<p>URL 查询（在 URL 最后添加'?'开头的查询），<code>skip</code> 是跳过的记录数，<code>limit</code> 是最多显示的记录数，这两个参数可选。</p>
<pre><code>"skip": 0
"limit": 50</code></pre>
<p><strong>输出：</strong></p>
<p>成功则返回 HTTP code 200。内容为 json。</p>
<p>注：在返回的用户的信息中，<code>groups</code> 键（用户的组列表）为空，若要获取组列表，请使用 <code>GET /user/&lt;user_id&gt;</code> 接口。</p>
<pre><code>{
  "users": [
    {
      "id": "xxxxx",
      "tag": {
          "key1": "value1",
          "key2": "value2"
      },
      "images": [
          "id1",
          "id2"
      ],
      "groups": [],
      "disabled": false
    },
    ...
  ]
}</code></pre>
<h4 id="post-user"><code>POST /user</code></h4>
<p>新建一个用户。</p>
<p><strong>输入：</strong></p>
<p>POST form，<code>tag</code> 是一个任意的 json，表示这个用户附加的自定义信息。</p>
<pre><code>"tag":              // 可选
{
  "key1": "value1",
  "key2": "value2"
}</code></pre>
<p><strong>输出：</strong></p>
<p>成功则返回 HTTP code 201。</p>
<p>内容为新创建的用户的信息。</p>
<pre><code>{
  "id": "xxxxx",
  "tag": {
      "key1": "value1",
      "key2": "value2"
  },
  "images": [
      "id1",
      "id2"
  ],
  "groups": [],
  "disabled": false
}</code></pre>
<h4 id="get-useruser_id"><code>GET /user/&lt;user_id&gt;</code></h4>
<p>获取 <code>user_id</code> 的用户的信息，以及用户所属的组的列表。</p>
<p><strong>输入：</strong></p>
<p>URL 参数 <code>user_id</code>。</p>
<p><strong>输出：</strong></p>
<p>成功则返回 HTTP code 200。</p>
<pre><code>{
  "id": "xxxxx",
  "tag": {
      "key1": "value1",
      "key2": "value2"
  },
  "images": [
      "id1",
      "id2"
  ],
  "groups": [
    "aaaaaa",
    "bbbbbb",
    ...
  ],
  "disabled": false
}</code></pre>
<h4 id="put-useruser_id"><code>PUT /user/&lt;user_id&gt;</code></h4>
<p>修改 <code>user_id</code> 的用户的信息。</p>
<p><strong>输入：</strong></p>
<p>URL 参数 <code>user_id</code>。</p>
<p>POST form <code>tag</code> 是要修改成的新信息。</p>
<pre><code>"tag":
{
  "key1": "value1",
  "key2": "value2"
}</code></pre>
<p><strong>输出：</strong></p>
<p>成功则返回 HTTP code 201。</p>
<pre><code>{
  "id": "xxxxx",
  "tag": {
      "key1": "value1",
      "key2": "value2"
  },
  "images": [
      "id1",
      "id2"
  ],
  "groups": [
    "aaaaaa",
    "bbbbbb",
    ...
  ],
  "disabled": false
}</code></pre>
<h4 id="delete-useruser_id"><code>DELETE /user/&lt;user_id&gt;</code></h4>
<p>删除 <code>user_id</code> 的用户。</p>
<p><strong>输入：</strong></p>
<p>URL 参数 <code>user_id</code>。</p>
<p><strong>输出：</strong></p>
<p>成功则返回 HTTP code 204。</p>
<h4 id="post-useruser_idimage"><code>POST /user/&lt;user_id&gt;/image</code></h4>
<p>上传用户底库照片，从而向底库中增加 Feature。</p>
<p>如果该用户的底库照片数量已达上限（典型地，为 3 张图片），则该接口报错。</p>
<p><strong>输入：</strong></p>
<p>URL 参数 <code>user_id</code>。</p>
<p>POST form：</p>
<pre><code>"image"                  // 'multipart/form-data' 上传的图片文件
"quality_threshold": 0.9 // 质量阈值</code></pre>
<p><strong>输出：</strong></p>
<p>成功则返回 HTTP code 200。</p>
<pre><code>{
  "image_id": "xxxx", // 如果质量太差未加入，则 image_id 为""
  "quality": 0.8,
  "user": {
    "id": "id",
    "tag": {
        "key1": "value1",
        "key2": "value2"
    },
    "images": [
        "xxxx",
        ...
    ],
    "groups": [
      "aaaaaa",
      "bbbbbb",
      ...
    ],
    "disabled": false
  }
}</code></pre>
<h4 id="delete-useruser_idimageimage_id"><code>DELETE /user/&lt;user_id&gt;/image/&lt;image_id&gt;</code></h4>
<p>删除用户底库照片，从而将底库 Feature 一并删除。</p>
<p><strong>输入：</strong></p>
<p>URL 参数 <code>user_id</code>, <code>image_id</code>。</p>
<p><strong>输出：</strong></p>
<p>成功则返回 HTTP code 204。</p>
<pre><code>{
  "id": "xxxxx",
  "tag": {
      "key1": "value1",
      "key2": "value2"
  },
  "images": [
      "id1",
      ...
  ],
  "groups": [
    "aaaaaa",
    "bbbbbb",
    ...
  ],
  "disabled": false
}</code></pre>
<h4 id="get-user_query"><code>GET /user_query</code></h4>
<p>通过条件查询用户。</p>
<p>可以通过 <code>tag</code> 及 <code>group_ids</code> 进行查询。<code>group_ids</code> 指定用户所属的组， <code>group_ids</code>中的任何一个组包含该用户即满足条件。<code>tag</code> 查询用户的 <code>tag</code> 中包含的内容，可以指定简单的 (key, value) 对，也可以指定 (key, list)，其中 <code>list</code> 是一个列表，<code>tag</code> 中的 <code>key</code> 要包含所有 <code>list</code> 中的成员。<code>tag</code> 的查询也可采用 MongoDB 的查询语法，指定 (key, object)，其中 <code>object</code> 是一个 MongoDB 的查询。<code>tag</code> 查询的所有 key 之间取交集。</p>
<p><strong>输入：</strong></p>
<p>URL 查询（在 URL 最后添加'?'开头的查询），包含以下参数：</p>
<pre><code>"query": // 可选
{
    "tag": {
      "key1": "value1",
      "key2": ["value2", "value3"]
    },
    "group_ids": ["id1", "id2", "id3"]
}

"skip": 0   // 可选
"limit": 50 // 可选</code></pre>
<p><strong>输出：</strong></p>
<p>成功则返回 HTTP code 200。</p>
<p>注：在返回的组的信息中，<code>groups</code> 键（用户的组列表）为空，若要获取组列表，请使用 <code>GET /user/&lt;user_id&gt;</code> 接口。</p>
<pre><code>{
  "users": [
    {
      "id": "xxxxx",
      "tag": {
          "key1": "value1",
          "key2": "value2"
      },
      "images": [
          "id1",
          "id2"
      ],
      "groups": [],
      "disabled": false
    },
    ...
  ]
}</code></pre>
<h4 id="get-imageimage_id"><code>GET /image/&lt;image_id&gt;</code></h4>
<p>返回 <code>image_id</code> 对应的图像。</p>
<p><strong>输入：</strong></p>
<p>URL 参数 <code>image_id</code>。</p>
<p><strong>输出：</strong></p>
<p>一个 JPEG 文件。</p>
<h3 id="静态识别接口">静态识别接口</h3>
<h4 id="post-analyze"><code>POST /analyze</code></h4>
<p>人脸参数分析 API。</p>
<p><strong>输入：</strong></p>
<p>POST form，使用 <code>multipart/form-data</code> 方式上传图片。</p>
<pre><code>"image" // 图片文件</code></pre>
<p><strong>输出：</strong></p>
<pre><code>{
  "attr": {
    "age": 19.49117088317871,
    "gender": [
      0.00010299873974872753,
      0.9998970031738281
    ],
    "minority": 0.0002888996386900544,
    "left_eye": [
      0.00000010128961491773225,
      0.0000000000000008704703489048268,
      0.9999998807907104,
      0.000000008516236604805272,
      0.000000010287307006251467,
      0.0000000006637915772600422
    ],
    "right_eye": [
      0.000000769306041092932,
      0.0000000000003258959671136735,
      0.999995231628418,
      0.0000023143618363974383,
      0.0000018107311916537583,
      0.00000002786819841560373
    ],
    "pose": {
      "roll": -0.05751202628016472,
      "pitch": -0.12543238699436188,
      "yaw": -0.14415279030799866
    },
    "quality": {
      "illumination": 0,
      "pose": 0,
      "expression": 0,
      "blurness": 0.0003366805030964315,
      "occlusion": 0
    }
  },
  "face_info": {
    "rect": {
      "left": 51,
      "top": 99,
      "right": 305,
      "bottom": 352
    },
    "quality": 0.9996633194969036,
    "brightness": 137.6202421337649,
    "std_deviation": 17.072438764485213
  }
}</code></pre>
<p><code>attr</code> 中的各项属性含义如下：</p>
<ul>
<li><code>age</code>：估计年龄；</li>
<li><code>gender</code>：性别置信度，是一个长度为 2 的数组，含义分别为女性和男性的置信度；</li>
<li><code>minority</code>：少数民族置信度；</li>
<li><code>left_eye</code> 及 <code>right_eye</code>：双眼状态置信度，是一个长度为 6 的数组，含义分别为：<ul>
<li>没有戴眼镜，眼睛张开；</li>
<li>没有戴眼镜，眼睛合上；</li>
<li>戴普通眼镜，眼睛张开；</li>
<li>戴普通眼镜，眼睛合上；</li>
<li>戴墨镜；</li>
<li>有其它遮挡物。</li></ul></li>
<li><code>pose</code>：人脸在空间中的旋转情况。如果以人的左方向为 x 轴，正上方为 y 轴，正前方为 z 轴：<ul>
<li><code>roll</code>：人脸在 xy 平面中的旋转角度，即顺/逆时针旋转；</li>
<li><code>pitch</code>：人脸在 yz 平面中的旋转角度，即仰头/低头；</li>
<li><code>yaw</code>：人脸在 xz 平面中的旋转角度，即左转/右转。</li></ul></li>
<li><code>quality</code>：用于计算人脸质量的其它值。目前只有 <code>blurness</code> 已实现，表示人脸的模糊程度，在 [0, 1] 间取值，数值越小越清晰。</li>
</ul>
<h4 id="post-compare"><code>POST /compare</code></h4>
<p>进行人脸比对。</p>
<p><strong>输入：</strong></p>
<p>使用 <code>multipart/form-data</code> 方式上传图片。或者通过 POST form 提供 Feature。</p>
<pre><code>"image1"             // 图片文件
或者
"feature1": "string" // Feature 字符串

"image2"             // 图片文件
或者
"feature2": "string" // Feature 字符串</code></pre>
<p><strong>输出：</strong></p>
<p>注：使用 <code>feature</code> 代替 <code>image</code> 时，输出中不包含 <code>face_info</code>。</p>
<pre><code>{
  "face_info_1": {
    "rect": {
      "left": 51,
      "top": 99,
      "right": 305,
      "bottom": 352
    },
    "quality": 0.9996633194969036,
    "brightness": 137.6202421337649,
    "std_deviation": 17.072438764485213
  },
  "face_info_2": {
    "rect": {
      "left": 101,
      "top": 145,
      "right": 342,
      "bottom": 386
    },
    "quality": 0.999677713465644,
    "brightness": 114.95466675849245,
    "std_deviation": 39.382439974283955
  },
  "same": true,
  "score": 92.90785217285156
}</code></pre>
<p>其中 <code>score</code> 表示两张脸的相似程度，将一张脸识别成另一张脸的置信度。</p>
<h4 id="post-recognize"><code>POST /recognize</code></h4>
<p>静态识别接口。</p>
<p><strong>输入：</strong></p>
<p>使用 <code>multipart/form-data</code> 方式上传图片。</p>
<p>POST form：</p>
<pre><code>"group_id": "xxxxx" // 组 ID
"feature": "string" // Feature 字符串
或者
"image"             // 图片文件
"quality": "on"     // "on" 表示开启质量判断
"limit": 10         // 结果数量限制</code></pre>
<p><strong>输出：</strong></p>
<pre><code>{
  "candidates": [
    {
      "id": "PERSON_ID_1",
      "tag": "PERSON_TAG_1",
      "confidence": 96.209435,
      "feature_id": 1
    },
    {
      "id": "PERSON_ID_2",
      "tag": "PERSON_TAG_2",
      "confidence": 73.096985,
      "feature_id": 0
    },
    {
      "id": "PERSON_ID_3",
      "tag": "PERSON_TAG_3",
      "confidence": 69.694695,
      "feature_id": 0
    }
  ],
  "face_info": {
    "rect": {
      "left": 51,
      "top": 99,
      "right": 305,
      "bottom": 352
    },
    "quality": 0.9996633194969036,
    "brightness": 137.6202421337649,
    "std_deviation": 17.072438764485213
  },
  "person": {
    "id": "PERSON_ID_1",
    "tag": "PERSON_TAG_1",
    "confidence": 96.209435,
    "feature_id": 1
  },
  "quality_ok": true,
  "recognized": true
}</code></pre>
<p><code>candidates</code> 和 <code>person</code> 中的 <code>feature_id</code> 指计算出该 <code>confidence</code> 的 Feature 在编号为 <code>id</code> 的用户的 Feature 数组中的下标。</p>
<p><code>quality_ok</code> 表示质量判断是否通过，<code>recognized</code> 表示上传的图片/Feature 与 <code>person</code> 中所描述的用户是否是同一个人。</p>
<h4 id="post-verify"><code>POST /verify</code></h4>
<p>静态单人比对 API。</p>
<p><strong>输入：</strong></p>
<p>使用 <code>multipart/form-data</code> 方式上传图片。</p>
<p>POST form：</p>
<pre><code>"group_id": "xxxxx" // 组 ID
"user_id": "yyyyy"  // 用户 ID
"feature": "string" // Feature 字符串
或者
"image"             // 图片文件
"quality": "on"     // "on" 表示开启质量判断</code></pre>
<p><strong>输出：</strong></p>
<pre><code>{
  "face_info": {
    "rect": {
      "left": 51,
      "top": 99,
      "right": 305,
      "bottom": 352
    },
    "quality": 0.9996633194969036,
    "brightness": 137.6202421337649,
    "std_deviation": 17.072438764485213
  },
  "person": {
    "id": "PERSON_ID_1",
    "tag": "PERSON_TAG_1",
    "confidence": 96.209435,
    "feature_id": 1
  },
  "quality_ok": true,
  "recognized": true
}</code></pre>
<h2 id="错误代码">错误代码</h2>
<p>ARC所提供的所有接口，如果出现错误，会按照下面的方式进行报错。</p>
<p>首先，HTTP Status Code 会提示错误的类别，主要有以下几种：</p>
<ul>
<li>400 Bad Request：用户的输入不满足要求；</li>
<li>401 Unauthorized：用户需要登入；</li>
<li>403 Forbidden：登入用户的权限不足；</li>
<li>404 Not Found：用户请求的资源不存在；</li>
<li>500 Internal Server Error：服务器出现异常。</li>
</ul>
<p>同时，在返回体中会以下面的格式返回具体错误内容：</p>
<pre><code>{
  "code": 100,
  "description": "Not found",
  "raw": "Something happened"
}</code></pre>
<p>其中，<code>code</code> 代表错误码（与 HTTP Status Code 不同）；<code>description</code> 是对错误码的具体解释，与错误码对应；<code>raw</code> 为服务器内部接收到的原生异常。如果出现意外错误，提供 <code>raw</code> 中的信息或许可以提供帮助。</p>
<p>错误码的列表如下：</p>
<ul>
<li>0：未知错误；</li>
<li>100：资源不存在；</li>
<li>101：解析输入出现错误；</li>
<li>102：将结果转换为 JSON 等输出格式是出现错误；</li>
<li>103：无法连接到 Leaf 或其他服务器；</li>
<li>104：请求被 Leaf 拒绝。例如输入图像没有检测到人脸；</li>
<li>105：执行 SQL 语句时出现错误；</li>
<li>106：准备 SQL 语句时出现错误；</li>
<li>107：其他 SQL 错误；</li>
<li>108：执行 MongoDB 语句时出现错误；</li>
<li>109：IO 错误；</li>
<li>110：参数不足；</li>
<li>111：取得上传文件失败；</li>
<li>112：Handle 尚未启动；</li>
<li>113：达到单个User的底库图片上限；</li>
<li>114：系统正在重启；</li>
<li>200：尚未登录；</li>
<li>201：权限不足。</li>
</ul>
</article>
</body>
</html>
